package blackhouse

import (
	"runtime/debug"
	"sync"
	"time"

	"gitee.com/clearluo/gotools/util/ipratelimit/internal/typ"

	"gitee.com/clearluo/gotools/util"

	"gitee.com/clearluo/gotools/zaplog"
)

type ipBlackT struct {
	endUnix int64 // 小黑屋结束时间
}
type BlackHouseT struct {
	ips map[typ.IPInt]*ipBlackT
	sync.RWMutex
}

func NewBlackHouseT() *BlackHouseT {
	o := &BlackHouseT{
		ips:     make(map[typ.IPInt]*ipBlackT),
		RWMutex: sync.RWMutex{},
	}
	o.tickerClean()
	return o
}
func (b *BlackHouseT) List() (ipList []string) {
	b.RLock()
	for ipInt := range b.ips {
		ipList = append(ipList, ipInt.String())
	}
	b.RUnlock()
	return
}
func (b *BlackHouseT) tickerClean() {
	go func() {
		for {
			func() {
				defer func() {
					if err := recover(); err != nil {
						zaplog.Warn("BlackHouseT.tickerClean panic:%v,stack:%v", err, string(debug.Stack()))
					}
				}()
				ticker := time.NewTicker(time.Minute)
				for {
					select {
					case <-ticker.C:
						b.clear()
					}
				}
			}()
			time.Sleep(time.Second)
		}
	}()
}

func (b *BlackHouseT) clear() {
	defer util.Profiling("BlackHouseT.clear()")()
	b.Lock()
	defer b.Unlock()
	now := time.Now().Unix()
	for k, v := range b.ips {
		if now > v.endUnix {
			delete(b.ips, k)
		}
	}
}

func (b *BlackHouseT) IsBlack(ipInt typ.IPInt) (ok bool) {
	b.RLock()
	defer b.RUnlock()
	_, ok = b.ips[ipInt]
	return ok
}

func (b *BlackHouseT) AddBlack(ipInt typ.IPInt, blackDuration time.Duration) {
	zaplog.Debugf("BlackHouseT.AddBlack:%v", ipInt)
	b.Lock()
	defer b.Unlock()
	v, ok := b.ips[ipInt]
	if ok {
		v.endUnix = time.Now().Add(blackDuration).Unix()
	} else {
		b.ips[ipInt] = &ipBlackT{endUnix: time.Now().Add(blackDuration).Unix()}
	}
}
